package com.jsu.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jsu.user.mapper.IntegralRuleMapper;
import com.jsu.user.mapper.PointDetailMapper;
import com.jsu.user.mapper.WebUserMapper;
import com.jsu.user.service.WebRuleService;
import com.jsu.user.service.WebUserService;
import lombok.extern.slf4j.Slf4j;
import org.example.model.common.dtos.ResponseResult;
import org.example.model.common.enums.RedisCodeEnum;
import org.example.model.common.enums.WebHttpCodeEnum;
import org.example.model.user.dto.RuleDto;
import org.example.model.user.enums.RuleEnums;
import org.example.model.user.pojo.PointDetail;
import org.example.model.user.pojo.Rule;
import org.example.model.user.pojo.User;
import org.example.utils.common.IdsUtils;
import org.example.utils.common.RedisCache;
import org.example.utils.common.WebJwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.sql.Date;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
@Transactional
public class WebRuleServiceImpl extends ServiceImpl<IntegralRuleMapper, Rule> implements WebRuleService{
    @Autowired(required = false)
    private IntegralRuleMapper integralRuleMapper;
    @Autowired
    private PointDetailServiceImpl pointDetailService;
    @Autowired
    private WebUserServiceImpl userService;
    @Autowired(required = false)
    private StringRedisTemplate redisCache;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private WebUserMapper userMapper;
    private static final ObjectMapper mapper = new ObjectMapper();

    /**
     * 获取所有积分规则
     * @return 积分规则集合
     */
    @Override
    public ResponseResult getRule() {
        //1.获取所有的rule规则
        List<Rule> ruleList = integralRuleMapper.selectList(null);
        log.info("ruleList:{}",ruleList.toArray().length);
        if(ruleList.size()==0)
            return ResponseResult.errorResult(WebHttpCodeEnum.DATA_NOT_EXIST,"暂时还没有积分规则");
        //2.将rule集合转化为ruleDto集合
        List<RuleDto> ruleDtoList = new ArrayList<>();
        //3根据手机号查询用户
//        String phone = request.getHeader("phone");
        String phone = "19574422385";
        log.info("当前用户的手机号码为:"+phone);
        User user = userMapper.selectByPhone(phone);
        if(user==null)
            return ResponseResult.errorResult(WebHttpCodeEnum.DATA_NOT_EXIST,"用户信息信息不存在");
        for (Rule r:ruleList) {
            //3.1判断当前用户当前规则是否签到过
            boolean signIn = false;
            // 3.2获取当前用户的积分获取明细记录
            //3.2.1从redis中获取
            String key = RedisCodeEnum.POINT_DETAIL.getMsg()+user.getPkId()+RedisCodeEnum.A.getMsg()+r.getPkId();
            PointDetail userPointDetail = getLatestPointDetail(key);
//            if (Objects.isNull(userPointDetail)) {
//                //3.2.1从数据库中获取
//                userPointDetail = pointDetailService.getUserPointDetail(user.getPkId(), r.getPkId());
//            }
            // 3.3判断用户是否已经签到过该规则
            if (Objects.isNull(userPointDetail) ){
                //3.3.1用户已经签到过
                if (r != null) {
                    //根据规则的重置信息判断是否可以再次签到
                    if (r.getIsReset() == RuleEnums.RESET.getNum()) { // 规则需要重置
                        // 获取上一次重置时间
                        LocalDateTime lastResetTime = r.getBeforeResetTime();
                        // 计算当前时间距离上一次重置时间的小时数
                        long hoursSinceLastReset = Duration.between(lastResetTime, LocalDateTime.now()).toHours();
                        // 判断是否在规定的重置间隔内
                        if (hoursSinceLastReset >= r.getResetTime()) {
                            // 可以再次签到
                            signIn = true;
                            //更新数据库中的上一次重置时间
                            LocalDateTime resetTime = lastResetTime.plusHours(r.getResetTime());
                            r.setBeforeResetTime(resetTime);
                            int count = integralRuleMapper.updateById(r);
                            if(count<=0)
                                return ResponseResult.errorResult(WebHttpCodeEnum.FAIL,"更新上一次重置时间失败");
                        }
                    }
                    else if(r.getIsReset()==RuleEnums.RESET_EVERYTIME.getNum()){
                        signIn = true;
                    }
                }
                else {
                    // 规则不存在
                    return ResponseResult.errorResult(WebHttpCodeEnum.FAIL,"规则不存在");
                }
            }
            else {
                // 用户未签到过
               signIn = true;
            }
            //3.4数据封装到ruleDto中，加入集合
            RuleDto ruleDto = new RuleDto(r.getPkId(),r.getName(),r.getPointNum(),r.getIntroduce(),signIn);
            ruleDtoList.add(ruleDto);
        }
        return ResponseResult.okResult(ruleDtoList);
    }

    /**
     * 管理端修改积分规则
     * @param rule
     * @return
     */
    @Override
    public ResponseResult updateRule(Rule rule) {
        int count = integralRuleMapper.updateById(rule);
        if(count<=0)
            return ResponseResult.errorResult(WebHttpCodeEnum.FAIL,"修改失败");
        return ResponseResult.okResult(200,"修改成功");
    }

    /**
     * 管理员端删除积分规则
     * @param id
     * @return
     */
    @Transactional
    @Override
    public ResponseResult deleteRule(int id) {
        int count = integralRuleMapper.deleteById(id);
        if(count<=0)
            return ResponseResult.errorResult(WebHttpCodeEnum.FAIL,"删除失败");
        return ResponseResult.okResult(200,"删除成功");
    }

    /**
     * 用户签到
     * @param id
     * @return
     */
    @Override
    @Transactional
    public ResponseResult ruleSignIn(int id,String token) {
        long phone = Objects.requireNonNull(WebJwtUtil.getClaimsBody(token)).get("id", Long.class);
        User user = userMapper.selectByPhone(String.valueOf(phone));
//        String phone = "19574422385";
//        log.info("当前用户的手机号码为:"+phone);
//        User user = userMapper.selectByPhone(phone);
//        log.info("当前用户的id为:{}",user.toString());
        if(user==null)
            return ResponseResult.errorResult(WebHttpCodeEnum.DATA_NOT_EXIST,"用户信息信息不存在");
        Rule rule = integralRuleMapper.selectById(id);
        log.info("当前用户的积分规则为:{}",rule.toString());
        Date now = Date.valueOf(LocalDateTime.now().toLocalDate());
        PointDetail pointDetail = new PointDetail(user.getPkId(),id,rule.getPointNum(),now);
        log.info("当前用户的积分规则为:{}",pointDetail.toString());
        //修改用户当前积分值
        user.setIntegral(user.getIntegral()+rule.getPointNum());
        boolean b = userService.updateById(user);
        if(!b) {
            return ResponseResult.errorResult(WebHttpCodeEnum.FAIL, "修改用户积分值失败");
        }
        //写进数据库
        return pointDetailService.addPointDetail(pointDetail);
    }

    /**
     * 添加积分和积分明细
     * @param id 规则id
     * @param userID 用户id
     * @return
     */
    public ResponseResult ruleSign(int id,int userID) {
        User user = userMapper.selectUserByID(userID);
        Rule rule = integralRuleMapper.selectById(id);
        log.info("当前用户的积分规则为:{}",rule.toString());
        Date now = Date.valueOf(LocalDateTime.now().toLocalDate());
        PointDetail pointDetail = new PointDetail(user.getPkId(),id,rule.getPointNum(),now);
        log.info("当前用户的积分规则为:{}",pointDetail.toString());
        //修改用户当前积分值
        user.setIntegral(user.getIntegral()+rule.getPointNum());
        boolean b = userService.updateById(user);
        if(!b) {
            return ResponseResult.errorResult(WebHttpCodeEnum.FAIL, "修改用户积分值失败");
        }
        //写进数据库
        return pointDetailService.addPointDetail(pointDetail);
    }

    /**
     * 新增规则
     * @param rule
     * @return
     */
    @Override
    public ResponseResult addRule(Rule rule) {
        int count = getBaseMapper().insert(rule);
        if (count<=0) {
            return ResponseResult.errorResult(WebHttpCodeEnum.FAIL);
        }
        return ResponseResult.okResult(WebHttpCodeEnum.SUCCESS);
    }

    /**
     * 搜索积分规则
     * @param keyword
     * @return
     */
    @Override
    public ResponseResult searchRule(String keyword) {
       List<Rule> ruleList = getBaseMapper().searchRuleByKeyword(keyword);
       if(ruleList.size()==0)
           return ResponseResult.errorResult(WebHttpCodeEnum.DATA_NOT_EXIST,"暂时还没有搜索到相关积分规则");
       return ResponseResult.okResult(ruleList);
    }

    /**
     * 批量删除积分规则
     * @param ids
     * @return
     */
    @Override
    public ResponseResult batchDeleteRule(Integer[] ids) {
        List<Integer> idList = ids.length>0? Arrays.asList(ids):new ArrayList<>();
        int count = getBaseMapper().deleteBatchIds(idList);
        if (count<=0) {
            return ResponseResult.errorResult(WebHttpCodeEnum.FAIL);
        }
        return ResponseResult.okResult(count);
    }

    /**
     * 获取积分规则列表
     *
     * @return
     */
    @Override
    public ResponseResult getRuleList() {
        List<Rule> ruleList = getBaseMapper().selectList(null);
        if(ruleList.size()==0)
            return ResponseResult.errorResult(WebHttpCodeEnum.DATA_NOT_EXIST,"暂时还没有积分规则");
        return ResponseResult.okResult(ruleList);
    }


    /**
     * 从 Redis 中获取最近的一条记录
     * @param key
     * @return pointDetail
     */
    public PointDetail getLatestPointDetail(String key) {
        List<String> pointDetails =redisCache.opsForList().range(key,0,-1);
        if(pointDetails.size()==0)
            return null;
        // 反序列化为List<PointDetail>
        List<PointDetail> pointDetailList = new ArrayList<>();
        for (String pointDetailJson : pointDetails) {
            try {
                PointDetail pointDetail = mapper.readValue(pointDetailJson, PointDetail.class);
                pointDetailList.add(pointDetail);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
        LocalDate now = LocalDate.now();
        PointDetail closestPointDetail = pointDetailList.get(0);
        long closestTimeDifference = Math.abs(Duration.between(now.atStartOfDay(),
                closestPointDetail.getTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay()).toMillis());
        for (PointDetail pointDetail : pointDetailList) {
            long timeDifference = Math.abs(Duration.between(now.atStartOfDay(),
                    pointDetail.getTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay()).toMillis());
            if (timeDifference < closestTimeDifference) {
                closestTimeDifference = timeDifference;
                closestPointDetail = pointDetail;
            }
        }


        return closestPointDetail;
    }


}
